package com.zone.editor.inter;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.widget.AppCompatImageView;

import com.zone.editor.pixel.PixelDrawingView;
import com.zone.editor.pixel.image.ColorItem;
import com.zone.editor.shape.AbstractShape;
import com.zone.editor.shape.BrushShape;
import com.zone.editor.shape.LineShape;
import com.zone.editor.shape.OvalShape;
import com.zone.editor.shape.RectangleShape;
import com.zone.editor.shape.ShapeAndPaint;
import com.zone.editor.shape.ShapeBuilder;
import com.zone.editor.shape.ShapeType;

import java.util.Stack;

public abstract class DrawingView extends View {
    private static final String TAG = DrawingView.class.getSimpleName();

//    protected static final Stack<ShapeAndPaint> drawShapes = new Stack<>();
//    protected static final Stack<ShapeAndPaint> redoShapes = new Stack<>();

    protected BrushViewChangeListener viewChangeListener;
    protected ShapeAndPaint currentShape;
    protected ShapeBuilder currentShapeBuilder;
    protected boolean isEnabled;
    protected Paint paint;

    // eraser parameters
    protected boolean isErasing = false;
    protected final float DEFAULT_ERASER_SIZE = 50.0f;
    protected float mBrushEraserSize = DEFAULT_ERASER_SIZE;

    public DrawingView(Context context) {
        this(context, null);
    }

    public DrawingView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DrawingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setupBrushDrawing();
    }

    public void setBrushViewChangeListener(BrushViewChangeListener brushViewChangeListener) {
        viewChangeListener = brushViewChangeListener;
    }
    // endregion

    protected Paint createPaint(boolean isErasing) {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.MITER);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));

        // apply shape builder parameters
        paint.setStrokeWidth(currentShapeBuilder.getShapeSize());
        paint.setAlpha(currentShapeBuilder.getShapeOpacity());
        paint.setColor(currentShapeBuilder.getShapeColor());

        return paint;
    }

    protected Paint createEraserPaint() {
        Paint paint = createPaint(isErasing);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        return paint;
    }

    private void setupBrushDrawing() {
        //Caution: This line is to disable hardware acceleration to make eraser feature work properly
        setLayerType(LAYER_TYPE_HARDWARE, null);
        setVisibility(View.GONE);
        currentShapeBuilder = new ShapeBuilder();
    }


//    @Override
//    protected void onDraw(Canvas canvas) {
//        for (ShapeAndPaint shape : drawShapes) {
//            shape.getShape().draw(canvas, shape.getPaint());
//        }
//    }

    /**
     * Handle touch event to draw paint on canvas i.e brush drawing
     *
     * @param event points having touch info
     * @return true if handling touch events
     */
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        if (isEnabled) {
            float touchX = event.getX();
            float touchY = event.getY();
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    onTouchEventDown(touchX, touchY);
                    break;
                case MotionEvent.ACTION_MOVE:
                    onTouchEventMove(touchX, touchY);
                    break;
                case MotionEvent.ACTION_UP:
                    onTouchEventUp(touchX, touchY);
                    break;
            }
            invalidate();
            return true;
        } else {
            return false;
        }
    }

    protected void onTouchEventDown(float touchX, float touchY) {
        createShape();
        if (currentShape != null && currentShape.getShape() != null) {
            currentShape.getShape().startShape(touchX, touchY);
        }
    }

    protected void onTouchEventMove(float touchX, float touchY) {
        if (currentShape != null && currentShape.getShape() != null) {
            currentShape.getShape().moveShape(touchX, touchY);
        }
    }

    private void onTouchEventUp(float touchX, float touchY) {
        if (currentShape != null && currentShape.getShape() != null) {
            currentShape.getShape().stopShape();
            endShape(touchX, touchY);
        }
    }

    protected abstract void createShape();

    protected abstract void endShape(float touchX, float touchY);

    public abstract boolean redo();

    // region eraser
    public abstract void brushEraser();

    public abstract void setBrushEraserSize(float brushEraserSize);

    public abstract float getEraserSize();
    // endregion

    // region Setters/Getters
    public abstract void setShapeBuilder(ShapeBuilder shapeBuilder);

    public abstract void enableDrawing(boolean brushDrawMode);

    public abstract boolean isDrawingEnabled();

    @VisibleForTesting
    public abstract ShapeAndPaint getCurrentShape();

    @VisibleForTesting
    public abstract ShapeBuilder getCurrentShapeBuilder();

////    @VisibleForTesting
//    public abstract Pair<Stack<ShapeAndPaint>, Stack<ShapeAndPaint>> getDrawingPath();

    public abstract void clearAll();

    public abstract boolean undo();
}
